/**
 * Copyright(c) 2015 Jade Techonologies Co., Ltd.
 *
 * History:
 *   15-4-24 下午1:43 Created by lyyang
 */
package com.jade.framework.base.util;

import java.io.*;
import java.security.KeyStore;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jade.framework.base.JadeSystem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.*;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;


/**
 * http连接工具
 *
 * @author <a href="mailto:lyyang@jade.com">lyyang</a>
 * @version 1.0 15-4-24 下午1:43
 */
public class HttpUtils
{

    protected static Log logger = LogFactory.getLog(HttpUtils.class);
    protected static ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, false);
    }

    public static CloseableHttpClient createP12SSLInsecureClient(String cerFile, String password)
    {
        try {
            //指定读取证书格式为PKCS12
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //读取本机存放的PKCS12证书文件
            FileInputStream instream = new FileInputStream(new File(cerFile));
            try {
                //指定PKCS12的密码(商户ID)
                keyStore.load(instream, password.toCharArray());
            }
            finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build();
            //指定TLS版本
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            //设置httpclient的SSLSocketFactory
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        }
        catch (Exception e) {
            JadeSystem.getLog().error("Can't create ssl http client:", e);
        }
        return HttpClients.createDefault();
    }

    public static CloseableHttpClient createSSLInsecureClient()
    {
        try {
            System.setProperty("https.protocols", "TLSv1,SSLv3,SSLv2Hello");
            SSLContext sslContext = SSLContexts.createSystemDefault();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
                    NoopHostnameVerifier.INSTANCE);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        }
        catch (Exception e) {
            JadeSystem.getLog().error("Can't create ssl http client:", e);
        }
        return HttpClients.createDefault();
    }

    public static <T> T postXmlHttp(String url, Object xml, Class respClass)
            throws Exception
    {
        CloseableHttpClient client = createSSLInsecureClient();
        return postXmlHttp(client, url, xml, respClass);
    }

    public static <T> T postXmlHttp(CloseableHttpClient client, String url, Object xml, Class respClass)
            throws Exception
    {
        JAXBContext context = JAXBContext.newInstance(xml.getClass());
        Marshaller marshaller = context.createMarshaller();
        StringWriter writer = new StringWriter();
        marshaller.marshal(xml, writer);
        String xmlStr = writer.toString();
        HttpPost post = new HttpPost(url);

        StringEntity entity = new StringEntity(xmlStr, "UTF-8");
        entity.setContentType("text/xml");
        entity.setContentEncoding("UTF-8");
        post.setEntity(entity);
        return doXmlPost(client, post, respClass);
    }

    public static <T> T doXmlPost(CloseableHttpClient client, HttpPost post, Class respClass)
            throws Exception
    {
        try {

            HttpResponse httpResponse = client.execute(post);
            StatusLine statusLine = httpResponse.getStatusLine();
            if (statusLine.getStatusCode() >= 300) {
                throw new Exception(statusLine.getReasonPhrase());
            }
            HttpEntity responseEntity = httpResponse.getEntity();
            String response = EntityUtils.toString(responseEntity);
            EntityUtils.consume(responseEntity);
            if (respClass.getSimpleName().equalsIgnoreCase("String")) {
                return (T) response;
            }

            JAXBContext context = JAXBContext.newInstance(respClass);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader reader = new StringReader(response);
            return (T) unmarshaller.unmarshal(reader);
        }
        catch (Exception e) {
            throw e;
        }
        finally {
            client.close();
        }
    }

    public static <T> T postHttp(String url, List<NameValuePair> params, Class respClass)
            throws Exception
    {
        HttpPost post = new HttpPost(url);
        if (params != null) {
            post.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));
        }
        return doPost(post, respClass);
    }

    public static <T> T postHttp(String url, Class respClass)
            throws Exception
    {
        return postHttp(url, null, respClass);
    }

    public static <T> T postJsonHttp(String url, Object args, Class respClass)
            throws Exception
    {
        String strJson = objectMapper.writeValueAsString(args);
        return postJsonHttp(url, strJson, respClass);
    }

    public static <T> T postJsonHttp(String url, String json, Class respClass)
            throws Exception
    {
        HttpPost post = new HttpPost(url);

        StringEntity entity = new StringEntity(json, "UTF-8");
        entity.setContentType("application/json");
        post.setEntity(entity);

        return doPost(post, respClass);
    }

    public static <T> T postFile(String url, String name, File file, Class respClass)
            throws Exception
    {
        HttpPost post = new HttpPost(url);
        FileBody fileBody = new FileBody(file);
        MultipartEntity reqEntity = new MultipartEntity();
        reqEntity.addPart(name, fileBody);
        post.setEntity(reqEntity);
        return doPost(post, respClass);
    }

    public static <T> T postFile(String url, String name, InputStream in, String mimeType, String filename,
                                 Class respClass)
            throws Exception
    {
        HttpPost post = new HttpPost(url);
        InputStreamBody body = new InputStreamBody(in, mimeType, filename);
        MultipartEntity reqEntity = new MultipartEntity();
        reqEntity.addPart(name, body);
        post.setEntity(reqEntity);
        return doPost(post, respClass);
    }

    public static <T> T doPost(HttpPost post, Class respClass)
            throws Exception
    {
        CloseableHttpClient client = createSSLInsecureClient();
        String response = null;
        try {

            HttpResponse httpResponse = client.execute(post);
            StatusLine statusLine = httpResponse.getStatusLine();
            if (statusLine.getStatusCode() >= 300) {
                throw new Exception(statusLine.getReasonPhrase());
            }
            HttpEntity responseEntity = httpResponse.getEntity();
            response = EntityUtils.toString(responseEntity);
            EntityUtils.consume(responseEntity);
            if (respClass.getSimpleName().equalsIgnoreCase("String")) {
                return (T) response;
            }
            logger.debug("response from http post:" + response);
            return (T) objectMapper.readValue(response, respClass);
        }
        catch (Exception e) {

            throw e;
        }
        finally {
            client.close();
        }
    }

    public static <T> T getHttp(String url, Class respClass)
            throws Exception
    {
        HttpGet get = new HttpGet(url);
        return doGet(get, respClass);
    }

    public static <T> T doGet(HttpGet get, Class respClass)
            throws Exception
    {
        CloseableHttpClient client = createSSLInsecureClient();
        try {

            HttpResponse httpResponse = client.execute(get);
            StatusLine statusLine = httpResponse.getStatusLine();
            if (statusLine.getStatusCode() >= 300) {
                throw new Exception(statusLine.getReasonPhrase());
            }
            HttpEntity responseEntity = httpResponse.getEntity();
            String response = EntityUtils.toString(responseEntity, "UTF-8");
            //            response = new String(response.getBytes("ISO-8859-1"), "UTF-8");
            EntityUtils.consume(responseEntity);
            if (respClass.getSimpleName().equalsIgnoreCase("String")) {
                return (T) response;
            }
            return (T) objectMapper.readValue(response, respClass);
        }
        catch (Exception e) {
            throw e;
        }
        finally {
            client.close();
        }
    }
}
